﻿using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Threading.Tasks;
using TranslatorService;

namespace GoogleTranslator
{
	// Get API Key from https://cloud.google.com
#pragma warning disable 1998
	public class GoogleTranslator : ITranslatorService
	{
		private const string baseUrl = "https://www.googleapis.com/language/translate/v2";
		private string apiKey;

		public TranslatorServiceCapabilities ServiceCapabilities
		{
			get
			{
				return TranslatorServiceCapabilities.DefinesWords |
					TranslatorServiceCapabilities.DetectsLanguage |
					TranslatorServiceCapabilities.HtmlText |
					TranslatorServiceCapabilities.TranslatesPhrases |
					TranslatorServiceCapabilities.PlainText |
					TranslatorServiceCapabilities.TranslatesWords |
					TranslatorServiceCapabilities.RequiresAuthentication;
			}
		}

		public string ServiceName
		{
			get { return "Google Translator"; }
		}

		public Uri ServiceURI
		{
			get { return new Uri("https://developers.google.com/translate/"); }
		}

		public string[] AuthenticationParameterDetail
		{
			get { return new string[] { "APIKey" }; }
		}

		public async Task<bool> AuthenticateAsync(params string[] options)
		{
			if (options.Length != 1)
				throw new ArgumentException();
			apiKey = options[0];
			return true;
		}

		public async Task<Dictionary<string, string>> GetSupportedLanguagesAsync(string locale = "en")
		{
			var codes = await CallMethodAsync<LanguagesRootObject>("languages", "prettyprint", "false", "target", locale);
			var d = new Dictionary<string, string>(100);
			//d.Add("en", "English"); d.Add("it", "Italian");
			foreach (var item in codes.data.languages)
				d.Add(item.language, item.name);
			return d;
		}

		public async Task<string> TranslateAsync(string sourceLang, string destLang, string value, InputFormat format = InputFormat.Text)
		{
			List<string> parameters = new List<string>();
			parameters.AddRange(new string[] { "q", value, "target", destLang, "prettyprint", "false", "format", format.ToString().ToLower() });
			if (!string.IsNullOrWhiteSpace(sourceLang))
				parameters.AddRange(new string[] { "source", sourceLang });
			var resp = await CallMethodAsync<TranslationRootObject>(null, parameters.ToArray());
			return resp.data.translations[0].translatedText;
		}

		public async Task<string> DetectLanguageAsync(string value)
		{
			var resp = await CallMethodAsync<DetectionRootObject>("detect", "prettyprint", "false", "q", value);
			return resp.data.detections[0].language;
		}

		public async Task<T> CallMethodAsync<T>(string methodName, params string[] parameters) where T : class
		{
			if (string.IsNullOrWhiteSpace(apiKey))
				throw new UnauthorizedAccessException();

			string[] newParams = new string[parameters.Length + 2];
			parameters.CopyTo(newParams, 2);
			newParams[0] = "key";
			newParams[1] = apiKey;
			return await HttpUtility.CallJsonMethodAsync<T>(baseUrl, methodName, null, newParams);
		}

		[DataContract]
		public class LanguagesRootObject
		{
			[DataMember]
			public Data data { get; set; }
			[DataContract]
			public class Data
			{
				[DataMember]
				public List<Language> languages { get; set; }
				[DataContract]
				public class Language
				{
					[DataMember]
					public string language { get; set; }
					[DataMember]
					public string name { get; set; }
				}
			}
		}

		[DataContract]
		private class TranslationRootObject
		{
			[DataMember]
			public Data data { get; set; }
			[DataContract]
			public class Data
			{
				[DataMember]
				public List<Translation> translations { get; set; }
				[DataContract]
				public class Translation
				{
					[DataMember]
					public string translatedText { get; set; }
				}
			}
		}

		[DataContract]
		private class DetectionRootObject
		{
			[DataMember]
			public Data data { get; set; }
			[DataContract]
			public class Data
			{
				[DataMember]
				public List<Detection> detections { get; set; }
				[DataContract]
				public class Detection
				{
					[DataMember]
					public string language { get; set; }
				}
			}
		}
	}
}
